package tank

import (
	"errors"
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
	"mime/multipart"
	"net/http"
	"server/config"
	db "server/data"
	"server/utils"
	"strings"
)

type RegTeamReq struct {
	Uid       string                `form:"uid" binding:"required"`
	Name      string                `form:"name" binding:"required"`
	FileA     *multipart.FileHeader `form:"fileA" binding:"required"`
	FileB     *multipart.FileHeader `form:"fileB" binding:"required"`
	FileC     *multipart.FileHeader `form:"fileC" binding:"required"`
	FileD     *multipart.FileHeader `form:"fileD" binding:"required"`
	GroupName string                `form:"group" binding:"oneof=A B"`
}

func (r RegTeamReq) valid() bool {
	if r.GroupName != GroupA && r.GroupName != GroupB {
		return false
	}
	return true
}

const (
	GroupA                = "A"
	GroupB                = "B"
	SourceFileASuffix     = "2022playerA.js"
	SourceFileBSuffix     = "2022playerB.js"
	FileAObfuscatedSuffix = "2022playerAObfuscated.js"
	FileBObfuscatedSuffix = "2022playerBObfuscated.js"
)

func RegTeam(c *gin.Context) {
	var regTeamReq RegTeamReq
	if err := c.ShouldBind(&regTeamReq); err != nil {
		c.JSON(http.StatusOK, gin.H{"status": http.StatusBadRequest, "desc": err.Error()})
		return
	}

	c.Set(config.RequestLogParamKey, utils.Struct2str(regTeamReq))
	uid := regTeamReq.Uid
	name := regTeamReq.Name
	group := regTeamReq.GroupName

	if r := db.DB.Debug().Model(db.Team{}).Where("name=?", name).Find(&struct{}{}); r.RowsAffected != 0 {
		c.JSON(http.StatusOK, gin.H{"status": http.StatusBadRequest,
			"data": gin.H{"regStatus": config.REG_GAME_DUPLI_NAME},
		})
		return
	}

	if r := db.DB.Debug().Model(db.Team{}).Where("uid=?", uid).Find(&struct{}{}); r.RowsAffected != 0 {
		c.JSON(http.StatusOK, gin.H{"status": http.StatusBadRequest,
			"data": gin.H{"regStatus": config.REG_GAME_ALREADY_REG},
		})
		return
	}

	uploadDir := config.TeamBaseDir + uid + "/"
	createUploadDirIfNotExists(uploadDir)
	if err := uploadFiles(c, uploadDir, []string{SourceFileASuffix, SourceFileBSuffix}, regTeamReq.FileA, regTeamReq.FileB); err != nil {
		utils.Error.Println(err.Error())
		c.JSON(http.StatusOK, gin.H{"status": http.StatusBadRequest, "desc": "upload file error"})
		return
	}
	if err := uploadFiles2S3(uid, []string{FileAObfuscatedSuffix, FileBObfuscatedSuffix}, regTeamReq.FileC, regTeamReq.FileD); err != nil {
		utils.Error.Println(err.Error())
		c.JSON(http.StatusOK, gin.H{"status": http.StatusBadRequest, "desc": "upload file error"})
		return
	}
	fileAS3Path := getS3FilePath(uid, FileAObfuscatedSuffix)
	fileBS3Path := getS3FilePath(uid, FileBObfuscatedSuffix)

	team := &db.Team{
		Uid:   regTeamReq.Uid,
		Name:  regTeamReq.Name,
		FileA: fileAS3Path,
		FileB: fileBS3Path,
		Group: group,
	}
	db.DB.Debug().Model(db.Team{}).Create(team)

	c.JSON(http.StatusOK, gin.H{
		"status": config.BusinessSuccessCode,
		"data":   gin.H{"regStatus": config.REG_GAME_SUCCESS},
	})
}

type UpdateTeamFileReq struct {
	Uid   string                `form:"uid" binding:"required"`
	FileA *multipart.FileHeader `form:"fileA" binding:"required"`
	FileB *multipart.FileHeader `form:"fileB" binding:"required"`
	FileC *multipart.FileHeader `form:"fileC" binding:"required"`
	FileD *multipart.FileHeader `form:"fileD" binding:"required"`
}

func UpdateTeamFile(c *gin.Context) {
	var updateTeamFileReq UpdateTeamFileReq
	if err := c.ShouldBind(&updateTeamFileReq); err != nil {
		c.JSON(http.StatusOK, gin.H{"status": http.StatusBadRequest, "desc": err.Error()})
		return
	}
	uid := updateTeamFileReq.Uid

	uploadDir := config.TeamBaseDir + uid + "/"
	if err := uploadFiles(c, uploadDir, []string{SourceFileASuffix, SourceFileBSuffix}, updateTeamFileReq.FileA, updateTeamFileReq.FileB); err != nil {
		c.JSON(http.StatusOK, gin.H{"status": http.StatusBadRequest, "desc": "upload file error"})
		return
	}
	if err := uploadFiles2S3(uid, []string{FileAObfuscatedSuffix, FileBObfuscatedSuffix}, updateTeamFileReq.FileC, updateTeamFileReq.FileD); err != nil {
		utils.Error.Println(err.Error())
		c.JSON(http.StatusOK, gin.H{"status": http.StatusBadRequest, "desc": "upload file error"})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"status": config.BusinessSuccessCode,
	})
}

type UnregTeamReq struct {
	Uid string `form:"uid" binding:"required"`
}

func UnregTeam(c *gin.Context) {
	var unregTeamReq UnregTeamReq
	if err := c.ShouldBindJSON(&unregTeamReq); err != nil {
		c.JSON(http.StatusOK, gin.H{"status": http.StatusBadRequest, "desc": err.Error()})
		return
	}

	c.Set(config.RequestLogParamKey, utils.Struct2str(unregTeamReq))

	db.DB.Debug().Model(db.Team{}).Where("uid=?", unregTeamReq.Uid).Unscoped().Delete(&db.Team{})
	c.JSON(http.StatusOK, gin.H{
		"status": config.BusinessSuccessCode,
	})
}

type TeamInfoReq struct {
	Uid string `form:"uid" binding:"required"`
}

func TeamInfo(c *gin.Context) {
	var teamInfoReq TeamInfoReq
	if err := c.ShouldBindQuery(&teamInfoReq); err != nil {
		c.JSON(http.StatusOK, gin.H{"status": http.StatusBadRequest, "desc": err.Error()})
		return
	}

	result := db.Team{}
	r := db.DB.Debug().Model(db.Team{}).Where("uid=?", teamInfoReq.Uid).Take(&result)

	if errors.Is(r.Error, gorm.ErrRecordNotFound) {
		c.JSON(http.StatusOK, gin.H{"status": config.BusinessSuccessCode,
			"data": gin.H{"regStatus": config.REG_GAME_INFO_UNREG},
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{"status": config.BusinessSuccessCode,
		"data": gin.H{"regStatus": config.REG_GAME_ALREADY_REG, "teamInfo": gin.H{
			"name": result.Name,
		}},
	})
	return
}

type TeamResp struct {
	Id    uint
	Uid   string
	Name  string
	FileA string
	FileB string
	Group string
}

func getFileName(path string) string {
	return path[strings.LastIndex(path, "/")+1:]
}

func filterTeams(teams []*TeamResp, users []*db.User) []*TeamResp {
	// 需要过滤掉不能进入赛事系统的邮箱注册用户
	filterMap := make(map[string]struct{})
	for _, user := range users {
		if user.JoinStatus == 1 {
			filterMap[user.Email] = struct{}{}
		}
	}

	result := []*TeamResp{}
	for _, t := range teams {
		if _, ok := filterMap[t.Uid]; !ok {
			t.FileA = getFileName(t.FileA)
			t.FileB = getFileName(t.FileB)
			result = append(result, t)
		}
	}
	return result
}

func Teams(c *gin.Context) {
	var teams []*TeamResp
	db.DB.Debug().Model(&db.Team{}).Select([]string{"id", "uid", "name", "file_a", "file_b", "group"}).Find(&teams)

	var uids []string
	for _, team := range teams {
		uids = append(uids, team.Uid)
	}

	var users []*db.User
	db.DB.Debug().Model(&db.User{}).Where("email IN ?", uids).Select("email", "join_status").Find(&users)

	teamsResp := filterTeams(teams, users)
	c.JSON(http.StatusOK, gin.H{"status": config.BusinessSuccessCode, "teams": teamsResp})
}
